/********************************************************/
/*	Copyright (C) 2016 Gong Li Bin		 	*/
/*	Project:	GlbLib-1.0.0			*/
/*	Author:		gong_libin			*/
/*	Date:		2016_06_01			*/
/*	File:		GlbPcap.cpp			*/
/********************************************************/

#include "GlbDef.h"
#include "GlbPro.h"
#include "GlbUtil.h"

#include <net/ethernet.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#define __FAVOR_BSD
#include <netinet/tcp.h>
#include <netinet/udp.h>
#include <getopt.h>

#include "pcap.h"

using namespace GlbNet;

bool g_bDisp = false;
bool g_bHead = false;
bool g_bBody = false;
static ULONG sg_ulCount = 0;

void GlbPcapCore(UCHAR* puszUser, const struct pcap_pkthdr* pstHead, const UCHAR* puszPacket)
{
	CGlbPro CPro;
	UINT uiLength = 0;
	struct timeval stTime;
	UCHAR* puszGlbPro = NULL;
	GLBPROKV_S* pstKv = NULL;
	struct ip* pstIP = NULL;
	struct tm* pstTime = NULL;
	struct tcphdr* pstTcp = NULL;
	struct udphdr* pstUdp = NULL;
	struct ether_header* pstEther = NULL;
	char szTime[GLB_BUFFER] = { 0 };

	gettimeofday(&stTime, NULL);
	pstTime = localtime(&stTime.tv_sec);
	strftime(szTime, GLB_BUFFER, "%H:%M:%S", pstTime);
	GLB_PRINT("Packet[%ld]: %d -> Time: %s.%ld\n", ++ sg_ulCount, pstHead->len, szTime, stTime.tv_usec);

	/* ether */
	pstEther = (struct ether_header*)puszPacket;

	/* ip */
	uiLength += sizeof(struct ether_header);
	pstIP = (struct ip*)(puszPacket + uiLength);
	GLB_PRINT("SrcAddr: %s\t", (char*)inet_ntoa(pstIP->ip_src));
	GLB_PRINT("DstAddr: %s\n", (char*)inet_ntoa(pstIP->ip_dst));

	/* tcp|udp */
	uiLength += pstIP->ip_hl * 4;
	switch (pstIP->ip_p) {
		case IPPROTO_TCP:
			GLB_PRINT("Protocol: TCP\n");
			pstTcp = (struct tcphdr*)(puszPacket + uiLength);
			GLB_PRINT("SrcPort: %d\t", ntohs(pstTcp->th_sport));
			GLB_PRINT("DstPort: %d\n", ntohs(pstTcp->th_dport));
			uiLength += pstTcp->th_off * 4;
			break;
		case IPPROTO_UDP:
			GLB_PRINT("Protocol: UDP\n");
			pstUdp = (struct udphdr*)(puszPacket + uiLength);
			GLB_PRINT("SrcPort: %d\t", ntohs(pstUdp->uh_sport));
			GLB_PRINT("DstPort: %d\n", ntohs(pstUdp->uh_dport));
			uiLength += sizeof(struct udphdr);
			break;
		default:
			break;
	}

	/* GlbPro */
	if (uiLength < pstHead->len) {
		puszGlbPro = (UCHAR*)(puszPacket + uiLength);
		if (true == g_bDisp) {
			GlbPacketDisplay(puszGlbPro, pstHead->len - uiLength);
		}
	}

	/* head */
	if (NULL != puszGlbPro) {
		CPro.GlbProParse(puszGlbPro, pstHead->len - uiLength);
		if (true == g_bHead) {
			GLB_PRINT("\nHead[%d]:\n", GLB_PRO_HEAD);
			CPro.GlbProDisplay();
		}
	}

	/* body */
	if (NULL != CPro.GlbProGetBody()) {
		if (true == g_bBody) {
			GLB_PRINT("\nBody[%d]:\n", CPro.GlbProGetLength());
			if (NULL != (pstKv = (GLBPROKV_S*)GlbMalloc(sizeof(GLBPROKV_S) * CPro.GlbProGetTotal()))) {
				GlbProKvInput(pstKv, CPro.GlbProGetTotal(), CPro.GlbProGetBody(), CPro.GlbProGetLength());
				GlbProKvDisplay(pstKv, CPro.GlbProGetTotal());
				GlbFree((void**)&pstKv);
			}
		}
	}

	GLB_PRINT("\n");

	return;
}

int main(int argc, char* argv[])
{
	int iIndex = 0;
	int iReturn = 0;
	bpf_u_int32 ulNet = 0;
	bpf_u_int32 ulMask = 0;
	pcap_t *pstPcap = NULL;
	struct bpf_program stFilter;
	char szError[GLB_PACKET] = { 0 };
	char szFilter[GLB_PACKET] = { 0 };
	char szDevice[GLB_PACKET] = { 0 };
	UCHAR uszPacket[GLB_PACKET] = { 0 };

	struct option stOptions[] = {
		{"eth", 1, 0, 'e'},
		{"port", 1, 0, 'p'},
		{"disp", 0, 0, 'd'},
		{"head", 0, 0, 'h'},
		{"body", 0, 0, 'b'},
		{0, 0, 0, 0},
	};

	while (true) {
		iReturn = getopt_long(argc, argv, "e:p:dhb", stOptions, &iIndex);
		if (iReturn < 0) {
			break;
		}
		switch (iReturn) {
			case 'e':	strncpy(szDevice, optarg, GLB_PACKET);
					break;
			case 'p':	snprintf(szFilter, GLB_PACKET, "port %s", optarg);
					break;
			case 'd':	g_bDisp = true;
					break;
			case 'h':	g_bHead = true;
					break;
			case 'b':	g_bBody = true;
					break;
			default:	return GLB_SUCCESS;
		}
	}

	if (argc < 3) {
		GLB_PRINT("Usage: %s -eeth1 -p8080 -h -b\n", argv[0]);
		GLB_PRINT("[*] -e or --eth:           set eth.\n");
		GLB_PRINT("[*] -p or --port:          set port.\n");
		GLB_PRINT("[0] -d or --disp:          display.\n");
		GLB_PRINT("[0] -h or --head:          show head.\n");
		GLB_PRINT("[0] -b or --body:          show body.\n");
		return GLB_SUCCESS;
	}

	pstPcap = pcap_open_live(szDevice, 65535, 1, 5, szError);
	if (NULL ==  pstPcap) {
		GLB_ERROR("%s\n", strerror(errno));
		return GLB_FAILURE;
	}

	if (pcap_lookupnet(szDevice, &ulNet, &ulMask, szError) < 0) {
		GLB_ERROR("%s\n", pcap_geterr(pstPcap));
		return GLB_FAILURE;
	}

	if (pcap_compile(pstPcap, &stFilter, szFilter, 1, ulMask) < 0) {
		GLB_ERROR("%s\n", pcap_geterr(pstPcap));
		return GLB_FAILURE;
	}

	if (pcap_setfilter(pstPcap, &stFilter) < 0) {
		GLB_ERROR("%s\n", pcap_geterr(pstPcap));
		return GLB_FAILURE;
	}

	if (pcap_loop(pstPcap, -1, GlbPcapCore, uszPacket) < 0) {
		GLB_ERROR("%s\n", pcap_geterr(pstPcap));
		return GLB_FAILURE;
	}

	pcap_freecode(&stFilter);
	pcap_close(pstPcap);

	return GLB_SUCCESS;
}
